package com.mkx.timerwheel;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 单层时间轮实现
 * 时间轮是一种高效的延迟任务调度数据结构
 */
public class SingleLevelTimeWheel {
    // 时间轮的槽数量
    private final int slotCount;
    // 每个槽的时间间隔（毫秒）
    private final long tickDuration;
    // 时间轮的总时间跨度
    private final long wheelRange;
    
    // 时间轮槽，每个槽存储一组任务
    private final TimerTaskList[] slots;
    // 当前指针位置
    private final AtomicInteger currentPos = new AtomicInteger(0);
    // 当前时间轮的启动时间
    private final long startTime;
    // 用于调度的延迟队列
    private final DelayQueue<TimerTaskList> delayQueue;
    // 使用原子引用和CAS操作代替读写锁，减少锁竞争
    private final AtomicInteger runningTasks = new AtomicInteger(0);
    // 任务列表对象池，减少对象创建
    private final ThreadLocal<List<TimerTask>> taskListPool = ThreadLocal.withInitial(ArrayList::new);
    // 工作线程
    private final Thread workerThread;
    // 是否运行中
    private volatile boolean running = false;
    
    /**
     * 构造函数
     * @param slotCount 槽数量
     * @param tickDuration 每个槽的时间间隔（毫秒）
     */
    public SingleLevelTimeWheel(int slotCount, long tickDuration) {
        if (slotCount <= 0 || tickDuration <= 0) {
            throw new IllegalArgumentException("slotCount and tickDuration must be positive");
        }
        this.slotCount = slotCount;
        this.tickDuration = tickDuration;
        this.wheelRange = slotCount * tickDuration;
        // 使用数组代替ArrayList，提高随机访问性能
        this.slots = new TimerTaskList[slotCount];
        this.delayQueue = new DelayQueue<>();
        this.startTime = System.currentTimeMillis();
        
        // 初始化槽
        for (int i = 0; i < slotCount; i++) {
            slots[i] = new TimerTaskList();
        }
        
        // 启动工作线程
        this.workerThread = new Thread(this::tick, "timewheel-worker");
        this.workerThread.setDaemon(true);
    }
    
    /**
     * 启动时间轮
     */
    public void start() {
        if (!running) {
            running = true;
            workerThread.start();
        }
    }
    
    /**
     * 停止时间轮
     */
    public void stop() {
        running = false;
        workerThread.interrupt();
    }
    
    /**
     * 添加任务到时间轮
     * @param task 要添加的任务
     * @return 是否添加成功
     */
    public boolean addTask(TimerTask task) {
        if (!running) {
            return false;
        }
        
        long delay = task.getDelay(TimeUnit.MILLISECONDS);
        
        // 如果任务已过期或延迟时间超过时间轮范围，则无法处理
        if (delay <= 0 || delay >= wheelRange) {
            return false;
        }
        
        // 计算任务应该放入哪个槽
        long currentTime = System.currentTimeMillis();
        long relativeTime = currentTime - startTime;
        long ticks = relativeTime / tickDuration;
        
        // 避免重复计算延迟时间
        long delayInTicks = delay / tickDuration;
        
        // 计算任务的执行时间点
        long executionTime = startTime + (ticks + delayInTicks + 1) * tickDuration;
        
        // 计算槽索引
        int slotIndex = (int)((ticks + delayInTicks + 1) % slotCount);
        
        task.setExecutionTime(executionTime);
        
        // 获取对应槽并添加任务
        TimerTaskList slot = slots[slotIndex];
        boolean added = slot.addTask(task);
        
        if (added) {
            runningTasks.incrementAndGet();
            // 如果这个槽第一次被添加任务，将其放入延迟队列
            if (slot.setExpiration(executionTime)) {
                delayQueue.offer(slot);
            }
        }
        return added;
    }
    
    /**
     * 时间轮的tick方法，不断处理到期的槽
     */
    private void tick() {
        while (running) {
            try {
                // 从延迟队列中获取到期的任务列表
                TimerTaskList expiredList = delayQueue.take();
                
                // 执行到期的任务，无需加锁
                expiredList.flush(task -> {
                    try {
                        task.run();
                        runningTasks.decrementAndGet();
                    } catch (Exception e) {
                        System.err.println("Error executing timer task: " + e.getMessage());
                    }
                });
                
                // 更新当前位置
                currentPos.set((currentPos.get() + 1) % slotCount);
            } catch (InterruptedException e) {
                if (!running) {
                    break;
                }
            }
        }
    }
    
    /**
     * 任务列表，每个槽对应一个这样的列表
     */
    private static class TimerTaskList implements Delayed {
        // 使用ConcurrentLinkedQueue代替ArrayList和锁，提高并发性能
        private final ConcurrentLinkedQueue<TimerTask> tasks = new ConcurrentLinkedQueue<>();
        // 使用原子引用存储过期时间
        private final AtomicLong expiration = new AtomicLong(-1);
        
        /**
         * 添加任务到列表
         */
        public boolean addTask(TimerTask task) {
            return tasks.offer(task);
        }
        
        /**
         * 设置过期时间
         * @return 如果是第一次设置或过期时间改变，返回true
         */
        public boolean setExpiration(long expiration) {
            return this.expiration.getAndSet(expiration) != expiration;
        }
        
        /**
         * 执行所有任务
         */
        public void flush(TaskHandler handler) {
            // 批量处理任务，避免多次创建集合
            TimerTask task;
            while ((task = tasks.poll()) != null) {
                handler.handle(task);
            }
            // 重置过期时间
            expiration.set(-1);
        }
        
        @Override
        public long getDelay(TimeUnit unit) {
            long remaining = expiration.get() - System.currentTimeMillis();
            return unit.convert(remaining, TimeUnit.MILLISECONDS);
        }
        
        @Override
        public int compareTo(Delayed o) {
            if (o instanceof TimerTaskList) {
                return Long.compare(this.expiration.get(), ((TimerTaskList) o).expiration.get());
            }
            return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS));
        }
        
        /**
         * 任务处理器接口
         */
        interface TaskHandler {
            void handle(TimerTask task);
        }
    }
}